package user

import (
	"context"
	"strconv"

	v1 "gitee.com/liangziyisheng/caas-platform-mc/api/user/v1"
	"gitee.com/liangziyisheng/caas-platform-mc/internal/consts"
	"gitee.com/liangziyisheng/caas-platform-mc/internal/dao"
	"gitee.com/liangziyisheng/caas-platform-mc/internal/model/entity"
	"gitee.com/liangziyisheng/caas-platform-mc/internal/service"
	"gitee.com/liangziyisheng/caas-platform-mc/utility/authorization"

	"github.com/gogf/gf/v2/errors/gcode"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/glog"
)

type sUser struct{}

var l = g.Log("logger for User")

func New() *sUser {
	return &sUser{}
}

func init() {
	l.SetFlags(glog.F_FILE_LONG)
	service.RegisterUser(New())
}

func (s *sUser) List(ctx context.Context, in *v1.ListInput) (out *v1.ListOutput, err error) {

	// l.Info(ctx, gctx.CtxId(ctx))

	cls := dao.User.Columns()

	md := dao.User.Ctx(ctx).Where(cls.TenantCode, in.TenantCode)
	if in.EmployeeNo != "" {
		md = md.Where(cls.EmployeeNo, in.EmployeeNo)
	}
	if in.Username != "" {
		md = md.Where(cls.Username, in.Username)
	}
	if in.Phone != "" {
		md = md.Where(cls.Phone, in.Phone)
	}
	if in.Fullname != "" {
		md = md.WhereLike(cls.Fullname, "%"+in.Fullname+"%")
	}

	out = &v1.ListOutput{}
	err = md.Limit((in.Page-1)*in.Size, in.Size).ScanAndCount(&out.Data, &out.Total, true)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.ListUserFailed, in))
	}
	return out, nil
}
func (s *sUser) Info(ctx context.Context, in *v1.InfoInput) (out *v1.InfoOutput, err error) {
	l.Info(ctx, "查询用户的策略列表和角色列表")
	out = &v1.InfoOutput{Roles: make([]entity.UserRole, 0), Id: in.Id}

	// 首先确定该用户存在
	user := entity.User{}
	cls := dao.User.Columns()
	err = dao.User.Ctx(ctx).Where(cls.Id, in.Id).Where(cls.TenantCode, in.TenantCode).Scan(&user)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.UserNotExist, in.Id))
	}
	if user.Id == 0 {
		return nil, gerror.NewCode(gcode.WithCode(consts.UserNotExist, in.Id))
	}

	// 在查询该用户角色
	e := authorization.NewEnforcer()
	// 查询用户关联角色
	roles, err := e.GetRolesForUser(strconv.FormatInt(in.Id, 10))
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCodef(consts.QueryUserRoleFailed, consts.QueryUserRoleFailed.Message(), in.Id)
	}
	err = dao.UserRole.Ctx(ctx).WhereIn(dao.UserRole.Columns().Name, roles).Scan(&out.Roles)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCodef(consts.QueryUserRoleFailed, consts.QueryUserRoleFailed.Message(), in.Id)
	}
	// e.GetFilteredGroupingPolicy(0, strconv.FormatInt(in.Id, 10))
	return out, nil
}

func (s *sUser) Add(ctx context.Context, in *v1.AddInput) (out *v1.AddOutput, err error) {
	id, err := dao.User.Ctx(ctx).Data(in.User).InsertAndGetId()
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.AddUserFailed, in.User))
	}
	return &v1.AddOutput{Id: id}, nil
}

func (s *sUser) Delete(ctx context.Context, in *v1.DeleteInput) (out *v1.DeleteOutput, err error) {
	// 先查询用户的详情
	user := entity.User{}
	cls := dao.User.Columns()
	err = dao.User.Ctx(ctx).Where(cls.Id, in.Id).Where(cls.TenantCode, in.TenantCode).Scan(&user)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.UserNotExist, in.Id))
	}
	if user.Id == 0 {
		return nil, gerror.NewCode(gcode.WithCode(consts.UserNotExist, in.Id))
	}
	// 先检查用户是否在用户组或者角色中关联
	var rows int
	ugcls := dao.GroupUser.Columns()
	rows, err = dao.GroupUser.Ctx(ctx).Where(ugcls.UserId, in.Id).Count()
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.QueryUserGroupUserFailed, in.Id))
	}
	if rows > 0 {
		return nil, gerror.NewCode(consts.UserAssignedInRole)
	}
	e := authorization.NewEnforcer()
	r, err := e.GetRolesForUser(strconv.FormatInt(user.Id, 10))
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.QueryGroupPolicyFailed, user.Username))
	}
	if len(r) > 0 {
		return nil, gerror.NewCode(consts.UserAssignedInRole)
	}

	rs, err := dao.User.Ctx(ctx).Where(cls.Id, user.Id).Where(cls.TenantCode, in.TenantCode).Delete()
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.DeleteFailed, in.Id))
	}
	out = &v1.DeleteOutput{}
	if out.Rows, err = rs.RowsAffected(); err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.DeleteFailed, in.Id))
	}
	if out.Rows == 0 {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.DeleteFailed, in.Id))
	}
	return out, nil

}

func (s *sUser) Update(ctx context.Context, in *v1.UpdateInput) (out *v1.UpdateOutput, err error) {
	cls := dao.User.Columns()
	out = &v1.UpdateOutput{}
	out.Rows, err = dao.User.Ctx(ctx).Data(in.User).Where(cls.Id, in.Id).Where(cls.TenantCode, in.TenantCode).UpdateAndGetAffected()
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.UpdateFailed, in.User))
	}
	if out.Rows == 0 {
		return nil, gerror.NewCode(gcode.WithCode(consts.UpdateFailed, in.Id))
	}
	return out, nil
}
